package tcp

import (
	"bytes"
	"cm2/util"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"os"
	"os/signal"
	"reflect"
	"syscall"
	"time"

	"github.com/kataras/golog"
)

// TODO: TCPClient 和 TCPServer 可以再提炼一层

// 基于增加的头和尾的思路，若后续有安全需求，可以增加加密、签名或者crc等一致性校验，避免攻击

// PackageFirstSigByte 包中第一个标记， 可作为 rune 标记
const PackageFirstSigByte byte = 0xb1

// PackageSecondSigByte 包中第二个标记， 可作为 rune 标记
const PackageSecondSigByte byte = 0xe6

// PackageTail 包尾连续 3 个 0xe3
var PackageTail = []byte{0xe3, 0xe3, 0xe3}

// PackageDataLengthSize 数据包中数据长度的大小 4位为 int32
const PackageDataLengthSize = 4

// PackageMinSize 数据包的最小长度
const PackageMinSize int = 6

const bufferLength = 512

// Header 用来描述 package 的头信息
type Header struct {
	F      byte
	S      byte
	Length int
}

// Package 通讯包的结构体
type Package struct {
	Header *Header
	Data   []byte
}

// NewPackage 创建一个新的数据包
func NewPackage(data []byte) *Package {
	return NewPackageWithSig(data, PackageFirstSigByte, PackageSecondSigByte)
}

// NewPackageWithSig 用两个前置字节标记来创建一个新的数据包
func NewPackageWithSig(data []byte, f, s byte) *Package {
	header := &Header{
		F:      f,
		S:      s,
		Length: len(data),
	}

	return &Package{
		Header: header,
		Data:   data,
	}
}

// ToBytes 将一个数据包转换成 byte[]
func (pkg *Package) ToBytes() ([]byte, error) {
	buff := bytes.NewBuffer([]byte{})
	var err, err2 error
	// 写标记
	if err = buff.WriteByte(pkg.Header.F); err != nil {
		return nil, err
	}
	if err = buff.WriteByte(pkg.Header.S); err != nil {
		return nil, err
	}

	// 写长度
	var lbs []byte
	lbs, err2 = util.Int32ToBytes(int32(pkg.Header.Length))
	if err2 != nil {
		return nil, err
	}
	if _, err = buff.Write(lbs); err != nil {
		return nil, err
	}

	// 写数据
	if _, err = buff.Write(pkg.Data); err != nil {
		return nil, err
	}

	// 写入固定包尾
	buff.Write(PackageTail)

	return buff.Bytes(), nil
}

// MaybePackage 检查指定的字节流是否可能为数据包
func MaybePackage(bs []byte) bool {
	return len(bs) > PackageMinSize
}

// ValidPackage 检查指定的字节流是否为有效包（同默认sig对比）
func ValidPackage(bs []byte) error {
	return ValidPackageWithSig(bs, PackageFirstSigByte, PackageSecondSigByte)
}

// ValidPackageWithSig 检查指定的字节流是否为有效包（同指定sig对比）
func ValidPackageWithSig(bs []byte, f, s byte) error {
	if MaybePackage(bs) {
		if bs[0] == f && bs[1] == s {
			return nil
		}
		return errors.New("header buffer sig not match")
	}
	return errors.New("header buffer length not match")
}

// TryResolvePackages 尝试从指定的字节流中解析数据包（可能为多个），并返回操作后的剩余字节流
func TryResolvePackages(bs []byte) ([]*Package, []byte, error) {
	return TryResolvePackagesWithSig(bs, PackageFirstSigByte, PackageSecondSigByte)
}

// TryResolvePackagesWithSig 尝试从指定的字节流中解析数据包（可能为多个），并返回操作后的剩余字节流
func TryResolvePackagesWithSig(bs []byte, f, s byte) ([]*Package, []byte, error) {
	pkgs := []*Package{}

	return resolvePackagesWithSig(pkgs, bs, f, s)
}

func resolvePackagesWithSig(pkgs []*Package, bs []byte, f, s byte) ([]*Package, []byte, error) {
	if len(bs) <= 0 {
		return pkgs, bs, nil
	}

	buff := bytes.NewBuffer(bs)
	var err error
	// 校验包
	if err = ValidPackageWithSig(bs, f, s); err != nil {
		return pkgs, bs, fmt.Errorf("invalid package buffer: %s", err)
	}
	// 偏移两位读取长度
	buff.Next(2)
	var pl int32
	if pl, err = util.BytesToInt32(buff.Next(PackageDataLengthSize)); err != nil {
		return pkgs, bs, fmt.Errorf("reading package data length failed: %s", err)
	}
	// 检查剩余长度是否满足data长度标记要求
	if int(pl) > len(bs)-PackageMinSize-len(PackageTail) {
		return pkgs, bs, errors.New("incomplete package data buffer, wait for next call")
	}

	// 读取数据，构建package， 添加到集合中
	data := buff.Next(int(pl))
	pkg := NewPackageWithSig(data, f, s)
	pkgs = append(pkgs, pkg)
	// 位移包尾
	buff.Next(len(PackageTail)) // 可用 seek 代替
	// 将剩余所有作为下次递归的内容
	rem := buff.Next(len(bs) - PackageMinSize - int(pl))
	// 对 rem 进行判断，如果不为 sig 。。直接丢弃
	if (len(rem) > 0 && rem[0] != f) || (len(rem) > 1 && rem[1] != s) {
		rem = []byte{}
	}

	// 递归调用
	return resolvePackagesWithSig(pkgs, rem, f, s)
}

// Package 完

// OnManualShutdown manual shutdown 看源码了解（封装好不，简单共用）
func OnManualShutdown(channel chan bool, cb func()) {
	channel = make(chan bool, 1)
	select {
	case <-channel:
		cb()
	}
}

// OnSysNotifyShutdown 在系统通知关闭时
func OnSysNotifyShutdown(cb func()) {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch,
		// kill -SIGINT XXXX or Ctrl+c
		os.Interrupt,
		syscall.SIGINT, // register that too, it should be ok
		// os.Kill  is equivalent with the syscall.Kill
		os.Kill,
		syscall.SIGKILL, // register that too, it should be ok
		// kill -SIGTERM XXXX
		syscall.SIGTERM,
	)
	select {
	case <-ch:
		cb()
	}
}

// SendPackage 发送数据包
func SendPackage(
	serial string,
	conn *net.TCPConn,
	data interface{},
	errFn func(data interface{}, err error),
	okFn func(data interface{}),
	logger *golog.Logger,
) error {
	return SendPackageWithSig(
		serial,
		conn,
		data,
		errFn,
		okFn,
		PackageFirstSigByte,
		PackageSecondSigByte,
		logger,
	)
}

// SendPackageWithSig 发送数据包
func SendPackageWithSig(
	serial string,
	conn *net.TCPConn,
	data interface{},
	errFn func(data interface{}, err error),
	okFn func(data interface{}),
	f, s byte,
	logger *golog.Logger,
) error {
	// 将包内容处理为 byte
	d, err := json.Marshal(data)
	if err != nil {
		return err
	}
	// 构建数据包
	pkg := NewPackageWithSig(d, f, s)

	// 通过异步发送 -> 调整为同步发送
	// go func(ser string, cn *net.TCPConn, p *Package) {
	func(ser string, cn *net.TCPConn, p *Package) {
		cnAddr := cn.RemoteAddr().String()
		var pbs, bs []byte
		var err error
		// 制作包
		if pbs, err = p.ToBytes(); err != nil {
			logger.Errorf("package encode faield %s. serial: %s", cnAddr, ser)
			if errFn != nil {
				errFn(data, err)
			}
		}
		logger.Infof("sending package to %s. len: %d serial: %s", cnAddr, len(pbs), ser)

		// 每次发送 512字节 数据
		buff := bytes.NewBuffer(pbs)
		end := make(chan bool, 1)
		remLen := len(pbs)
		sendingLen := bufferLength
		for len(end) < 1 {
			if remLen < bufferLength {
				// 不足512 完成发送
				bs = buff.Next(remLen)
				sendingLen = remLen
				end <- true
			} else {
				bs = buff.Next(bufferLength)
				remLen = remLen - bufferLength
				sendingLen = bufferLength
			}
			// 实际发送
			sendedLen, err := cn.Write(bs)
			if sendedLen != sendingLen {
				logger.Errorf("package sended not match. %d/%d serial: %s", sendedLen, sendingLen, cnAddr)
				if errFn != nil {
					errFn(data, err)
				}
				end <- true
			} else if err != nil {
				logger.Errorf("sending package faild. serial: %s error: %v", cnAddr, err)
				if errFn != nil {
					errFn(data, err)
				}
				end <- true
			}
		}
		<-end
		logger.Infof("package has been sended to %s. len: %d serial: %s", cnAddr, len(pbs), ser)
		if okFn != nil {
			okFn(data)
		}

	}(serial, conn, pkg)

	return nil
}

// HandleConnWithSig 处理连接
func HandleConnWithSig(
	conn *net.TCPConn,
	ts *Server,
	pkgFn func(*Package, *net.TCPConn, *Server, *golog.Logger),
	disConFn func(string, bool, *Server, *golog.Logger),
	logger *golog.Logger,
	retryTimes,
	retryDuration int,
	f, s byte,
) error {
	defer conn.Close()

	connAddr := conn.RemoteAddr().String()
	buff := make([]byte, bufferLength, bufferLength)
	received := []byte{}

	retry := 0
	var lastError error

	logger.Infof("%s enter reading package loop", connAddr)

	for {
		l, err := conn.Read(buff)
		// 检查错误
		if err != nil {
			logger.Warnf("read from connection %s failed, due to %s", connAddr, err)
			if reflect.TypeOf(lastError) != reflect.TypeOf(err) {
				retry = 0
				lastError = err
			} else {
				retry++
			}
			if retry > retryTimes {
				logger.Errorf("force close connection %s, due to %s", connAddr, err)
				break
			}
			time.Sleep(time.Second * time.Duration(retryDuration))
			continue
		}
		// 因为为无阻塞读取，当读取buff为0时，睡一下降低CPU消耗 /..其实我不知道怎么解决
		if l <= 0 {
			time.Sleep(time.Millisecond)
			continue
		}

		lastError = nil

		buff = bytes.TrimRight(buff, "\x00") // 去除尾部的 x00
		received = bytes.Join([][]byte{received, buff}, []byte{})

		// 检查是否可能是包
		if MaybePackage(received) {
			pkgs, rem, err := TryResolvePackagesWithSig(received, f, s)
			if err != nil {
				// 去除此处的警告
				// logger.Warnf("try resolve package failed with warning %s", err)
			}
			// 重置 received 继续下一次接收
			received = rem
			buff = nil
			buff = make([]byte, 64, 64)
			if len(pkgs) > 0 {
				for _, pkg := range pkgs {
					// 调用自定的 pkgFn
					if pkgFn != nil {
						pkgFn(pkg, conn, ts, logger)
					} else {
						logger.Warn("package handler is not specified")
					}
				}
			}
		}
	}

	if ts != nil {
		// 从map中移除
		delete(ts.conns, connAddr)
	}

	if disConFn != nil {
		disConFn(connAddr, true, ts, logger)
		logger.Infof("connection %s disconnected", connAddr)
	} else {
		logger.Warn("disconnected handler is not specified")
	}

	return lastError
}
